﻿using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using ST.Common.Util;
using ST.Common.Util.Security;
using ST.Common.Util.Text;
using ST.Common.Util.Xml;
using ST.WX.Common.Exceptions;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Xml;

namespace ST.WX.Common.Pay
{
	/// <summary>
	/// 微信请求/响应基类
	/// </summary>
	public class WXPayBase
	{
		public const string SIGN_TYPE_HMACSHA256 = "HMAC-SHA256";
		public const string SIGN_TYPE_MD5 = "MD5";

		/// <summary>
		/// 随机字符串
		/// </summary>
		[Required]
		[Display(Name = "随机字符串")]
		[StringLength(32)]
		protected string nonce_str { get; set; }

		/// <summary>
		/// 签名
		/// </summary>
		[Display(Name = "签名")]
		[StringLength(64)]
		protected string sign { get; set; }

		/// <summary>
		/// 签名类型（支持HMAC-SHA256和MD5）
		/// </summary>
		[Display(Name = "签名类型	")]
		//[StringLength(32)]
		public WXPaySignType sign_type { get; set; }

		/// <summary>
		/// 小程序ID
		/// </summary>
		[Required]
		[Display(Name = "小程序ID")]
		[StringLength(32)]
		public string appid { get; set; }

		/// <summary>
		/// 商户号
		/// </summary>
		[Required]
		[Display(Name = "商户号")]
		[StringLength(32)]
		public string mch_id { get; set; }

		/// <summary>
		/// 支付密钥
		/// </summary>
		[Display(Name = "支付密钥")]
		[JsonIgnore]
		public string mch_key { protected get; set; }

		public WXPayBase()
		{
		}

		public WXPayBase(string appID, string mchID, string mchKey, WXPaySignType signType)
		{
			appid = appID;
			mch_id = mchID;
			mch_key = mchKey;
			sign_type = signType;
		}

		/// <summary>
		/// 转化成url参数格式（该串不包含sign字段值且自动过滤值为空的键值）
		/// </summary>
		/// <returns></returns>
		public static string ToUrl(SortedDictionary<string, object> values)
		{
			if (values == null || values.Count == 0)
			{
				throw new WXPayException("支付参数不能为空");
			}
			StringBuilder buf = new StringBuilder();
			foreach (KeyValuePair<string, object> pair in values)
			{
				if (pair.Value == null)
				{
					continue;
					//throw new WXPayException("支付参数内部含有值为null的字段!");
				}

				if (pair.Key != "sign" && pair.Value.ToString() != "")//验证调用返回或微信主动通知签名时，传送的sign参数不参与签名
																	  //如果参数的值为空不参与签名
				{
					buf.Append(pair.Key + "=" + pair.Value + "&");
				}
			}
			return buf.ToString().TrimEnd('&');
		}

		/// <summary>
		/// 转换成json格式
		/// </summary>
		/// <returns></returns>
		public string ToJson(SortedDictionary<string, object> values)
		{
			if (values == null || values.Count == 0)
			{
				throw new WXPayException("支付参数不能为空");
			}
			return Newtonsoft.Json.JsonConvert.SerializeObject(values);
		}

		/// <summary>
		/// 格式化成能在Web页面上显示的结果（因为web页面上不能直接输出xml格式的字符串）
		/// </summary>
		/// <returns></returns>
		public string ToPrintStr(SortedDictionary<string, object> values)
		{
			if (values == null || values.Count == 0)
			{
				throw new WXPayException("支付参数不能为空");
			}
			StringBuilder buf = new StringBuilder();
			foreach (KeyValuePair<string, object> pair in values)
			{
				if (pair.Value == null)
				{
					continue;
					//throw new WXPayException("支付参数内部含有值为null的字段");
				}
				buf.AppendFormat("{0}={1}\n", pair.Key, pair.Value.ToString());
			}
			return HttpUtility.HtmlEncode(buf.ToString());
		}

		/// <summary>
		/// 生成签名，详见签名生成算法（sign字段不参加签名）
		/// </summary>
		/// <param name="key">支付密钥</param>
		/// <param name="signType"></param>
		/// <returns></returns>
		public string MakeSign(SortedDictionary<string, object> values)
		{
			return MakeSign(values, mch_key, sign_type);
		}

		/// <summary>
		/// 生成签名，详见签名生成算法（sign字段不参加签名）
		/// </summary>
		/// <param name="key">支付密钥</param>
		/// <param name="signType"></param>
		/// <returns></returns>
		public static string MakeSign(SortedDictionary<string, object> values, string mchKey, WXPaySignType signType)
		{
			//转url格式
			string str = ToUrl(values);
			str += "&key=" + mchKey;
			switch (signType)
			{
				case WXPaySignType.MD5:
					return EncryptHelper.MD5Hash(str, true);
				case WXPaySignType.HMACSHA256:
					return EncryptHelper.SHA256Hash(str, mchKey, true);
				default:
					throw new WXPayException("签名算法不支持");
			}
		}

		/// <summary>
		/// 校验返回签名（需要提前设置好mch_key，sign_type）
		/// </summary>
		/// <param name="key"></param>
		/// <param name="signType"></param>
		/// <returns></returns>
		public bool CheckSign(SortedDictionary<string, object> values)
		{
			if (values == null || values.Count == 0)
			{
				throw new WXPayException("支付参数不能为空");
			}
			if (values.ContainsKey("sign"))
			{
				string signRet = Convert.ToString(values["sign"]);
				//在本地计算新的签名
				string signCalc = MakeSign(values);
				if (signCalc != signRet)
				{
					throw new WXPayException("签名验证不一致");
				}
			}
			return true;
		}
	}
}
